// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/frame_host/render_frame_host_impl.h"

#include "base/macros.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/test_content_browser_client.h"

namespace content {

namespace {

    // Implementation of ContentBrowserClient that overrides
    // OverridePageVisibilityState() and allows consumers to set a value.
    class PrerenderTestContentBrowserClient : public TestContentBrowserClient {
    public:
        PrerenderTestContentBrowserClient()
            : override_enabled_(false)
            , visibility_override_(blink::WebPageVisibilityStateVisible)
        {
        }
        ~PrerenderTestContentBrowserClient() override { }

        void EnableVisibilityOverride(
            blink::WebPageVisibilityState visibility_override)
        {
            override_enabled_ = true;
            visibility_override_ = visibility_override;
        }

        void OverridePageVisibilityState(
            RenderFrameHost* render_frame_host,
            blink::WebPageVisibilityState* visibility_state) override
        {
            if (override_enabled_)
                *visibility_state = visibility_override_;
        }

    private:
        bool override_enabled_;
        blink::WebPageVisibilityState visibility_override_;

        DISALLOW_COPY_AND_ASSIGN(PrerenderTestContentBrowserClient);
    };

} // anonymous namespace

// TODO(mlamouri): part of these tests were removed because they were dependent
// on an environment were focus is guaranteed. This is only for
// interactive_ui_tests so these bits need to move there.
// See https://crbug.com/491535
using RenderFrameHostImplBrowserTest = ContentBrowserTest;

// Test that when creating a new window, the main frame is correctly focused.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
    IsFocused_AtLoad)
{
    EXPECT_TRUE(
        NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));

    // The main frame should be focused.
    WebContents* web_contents = shell()->web_contents();
    EXPECT_EQ(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
}

// Test that if the content changes the focused frame, it is correctly exposed.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
    IsFocused_Change)
{
    EXPECT_TRUE(
        NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));

    WebContents* web_contents = shell()->web_contents();

    std::string frames[2] = { "frame1", "frame2" };
    for (const std::string& frame : frames) {
        ExecuteScriptAndGetValue(
            web_contents->GetMainFrame(), "focus" + frame + "()");

        // The main frame is not the focused frame in the frame tree but the main
        // frame is focused per RFHI rules because one of its descendant is focused.
        // TODO(mlamouri): we should check the frame focus state per RFHI, see the
        // general comment at the beginning of this test file.
        EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
        EXPECT_EQ(frame, web_contents->GetFocusedFrame()->GetFrameName());
    }
}

// Tests focus behavior when the focused frame is removed from the frame tree.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, RemoveFocusedFrame)
{
    EXPECT_TRUE(
        NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));

    WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());

    ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "focusframe4()");

    EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
    EXPECT_EQ("frame4", web_contents->GetFocusedFrame()->GetFrameName());
    EXPECT_EQ("frame3",
        web_contents->GetFocusedFrame()->GetParent()->GetFrameName());
    EXPECT_NE(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);

    ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "detachframe(3)");
    EXPECT_EQ(nullptr, web_contents->GetFocusedFrame());
    EXPECT_EQ(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);

    ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "focusframe2()");
    EXPECT_NE(nullptr, web_contents->GetFocusedFrame());
    EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
    EXPECT_NE(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);

    ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "detachframe(2)");
    EXPECT_EQ(nullptr, web_contents->GetFocusedFrame());
    EXPECT_EQ(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);
}

// Test that a frame is visible/hidden depending on its WebContents visibility
// state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
    GetVisibilityState_Basic)
{
    EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,foo")));
    WebContents* web_contents = shell()->web_contents();

    web_contents->WasShown();
    EXPECT_EQ(blink::WebPageVisibilityStateVisible,
        web_contents->GetMainFrame()->GetVisibilityState());

    web_contents->WasHidden();
    EXPECT_EQ(blink::WebPageVisibilityStateHidden,
        web_contents->GetMainFrame()->GetVisibilityState());
}

// Test that a frame visibility can be overridden by the ContentBrowserClient.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
    GetVisibilityState_Override)
{
    EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,foo")));
    WebContents* web_contents = shell()->web_contents();

    PrerenderTestContentBrowserClient new_client;
    ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);

    web_contents->WasShown();
    EXPECT_EQ(blink::WebPageVisibilityStateVisible,
        web_contents->GetMainFrame()->GetVisibilityState());

    new_client.EnableVisibilityOverride(blink::WebPageVisibilityStatePrerender);
    EXPECT_EQ(blink::WebPageVisibilityStatePrerender,
        web_contents->GetMainFrame()->GetVisibilityState());

    SetBrowserClientForTesting(old_client);
}

} // namespace content
